<template>
  <div>
    <div
        ref="tippyElRef"
        :class="getClass"
        :style="getWrapperStyle"
        @contextmenu="handleContextMenu"
    >
      <img
          v-show="isReady"
          ref="imgElRef"
          :style="getImageStyle"
          :src="inSrc"
          :alt="alt"
          :crossorigin="crossorigin"
      />
    </div>
  </div>
</template>

<script setup>
import {useTippy} from "vue-tippy";
import {ref, h, computed, useAttrs, unref, onMounted, onUnmounted} from 'vue'
import ContentMenu from '@/components/MCropper/ContentMenu.vue'
import Cropper from "cropperjs";
import _ from 'lodash'
import {downloadBase64Image} from '@/utils/index.js'

defineOptions({
  name: 'MCropper'
})

function test() {
  console.log('test')
}

const defaultOptions = {
  aspectRatio: 1,
  zoomable: true,
  zoomOnTouch: true,
  zoomOnWheel: true,
  cropBoxMovable: true,
  cropBoxResizable: false,
  toggleDragModeOnDblclick: true,
  autoCrop: true,
  background: true,
  highlight: true,
  center: true,
  responsive: true,
  restore: true,
  checkCrossOrigin: true,
  checkOrientation: true,
  scalable: true,
  modal: true,
  guides: true,
  movable: false,
  rotatable: true,
  dragMode: 'move'
};

const props = defineProps({
  src: {type: String, required: true},
  alt: {type: String},
  circled: {type: Boolean, default: false},
  /** 是否可以通过点击裁剪区域关闭右键弹出的功能菜单，默认 `true` */
  isClose: {type: Boolean, default: true},
  realTimePreview: {type: Boolean, default: true},
  height: {type: [String, Number], default: "360px"},
  crossorigin: {
    type: [String, undefined],
    default: undefined
  },
  imageStyle: {type: Object, default: () => ({})},
  options: {type: Object, default: () => ({})}
})
const emit = defineEmits(["cropper", "readied", "error"]);
const tippyElRef = ref();
const imgElRef = ref();
const cropper = ref();
const inCircled = ref(props.circled);
const isInClose = ref(props.isClose);
const inSrc = ref(props.src);
const isReady = ref(false);
const imgBase64 = ref();

let scaleX = 1;
let scaleY = 1;

onMounted(init);

onUnmounted(() => {
  cropper.value.destroy();
  isReady.value = false;
  cropper.value = null;
  imgBase64.value = "";
  scaleX = 1;
  scaleY = 1;
});


const getWrapperStyle = computed(() => {
  return {height: `${props.height}`.replace(/px/, "") + "px"};
});
const debounceRealTimeCroppered = _.debounce(realTimeCroppered, 80);

function delay(time) {
  return new Promise(resolve => {
    setTimeout(() => {
      resolve();
    }, time);
  });
}

async function init() {
  const imgEl = unref(imgElRef);
  if (!imgEl) return;
  cropper.value = new Cropper(imgEl, {
    ...defaultOptions,
    ready: () => {
      isReady.value = true;
      realTimeCroppered();
      delay(400).then(() => emit("readied", cropper.value));
    },
    crop() {
      debounceRealTimeCroppered();
    },
    zoom() {
      debounceRealTimeCroppered();
    },
    cropmove() {
      debounceRealTimeCroppered();
    },
    ...props.options
  });
}

function realTimeCroppered() {
  props.realTimePreview && croppered();
}

function downloadFile() {
  downloadBase64Image(imgBase64.value, 'cropper.png')
}

function handCropper(event, arg) {
  if (event === "scaleX") {
    scaleX = arg = scaleX === -1 ? 1 : -1;
  }

  if (event === "scaleY") {
    scaleY = arg = scaleY === -1 ? 1 : -1;
  }
  arg && Array.isArray(arg)
      ? cropper.value?.[event]?.(...arg)
      : cropper.value?.[event]?.(arg);
}

function cropToggle() {
  inCircled.value = !inCircled.value;
  realTimeCroppered();
}

function beforeUpload(file) {
  const reader = new FileReader();
  reader.readAsDataURL(file);
  inSrc.value = "";
  reader.onload = e => {
    inSrc.value = e.target?.result;
  };
  reader.onloadend = () => {
    // init();
    cropper.value.replace(inSrc.value);
  };
  return false;
}

function croppered() {
  if (!cropper.value) return;
  const canvas = inCircled.value
      ? getRoundedCanvas()
      : cropper.value.getCroppedCanvas();
  // https://developer.mozilla.org/zh-CN/docs/Web/API/HTMLCanvasElement/toBlob
  canvas.toBlob(blob => {
    if (!blob) return;
    const fileReader = new FileReader();
    fileReader.readAsDataURL(blob);
    fileReader.onloadend = e => {
      if (!e.target?.result || !blob) return;
      imgBase64.value = e.target.result;
      emit("cropper", {
        base64: e.target.result,
        blob,
        info: {size: blob.size, ...cropper.value.getData()}
      });
    };
    fileReader.onerror = () => {
      emit("error");
    };
  });
}

function getRoundedCanvas() {
  const sourceCanvas = cropper.value.getCroppedCanvas();
  const canvas = document.createElement("canvas");
  const context = canvas.getContext("2d");
  const width = sourceCanvas.width;
  const height = sourceCanvas.height;
  canvas.width = width;
  canvas.height = height;
  context.imageSmoothingEnabled = true;
  context.drawImage(sourceCanvas, 0, 0, width, height);
  context.globalCompositeOperation = "destination-in";
  context.beginPath();
  context.arc(
      width / 2,
      height / 2,
      Math.min(width, height) / 2,
      0,
      2 * Math.PI,
      true
  );
  context.fill();
  return canvas;
}

const getImageStyle = computed(() => {
  return {
    height: props.height,
    maxWidth: "100%",
    ...props.imageStyle
  };
});

const attrs = useAttrs()

const getClass = computed(() => {
  return [
    attrs.class,
    {
      ["re-circled"]: inCircled.value
    }
  ];
});

const menuContent = h(ContentMenu, {
  beforeUpload,
  downloadFile,
  cropToggle,
  handCropper
  // cropper: cropper.value,
  // onCropper: handCropper,
  // onBeforeUpload: beforeUpload,
  // onCroppered: croppered,
  // onClose: () => {
  //   isInClose.value && document.documentElement.removeEventListener("click", destroy);
  // }
})

function handleContextMenu(event) {
  event.preventDefault();
  const {show, setProps, destroy, state} = useTippy(tippyElRef, {
    content: menuContent,
    arrow: false,
    theme: "light",
    trigger: "manual",
    interactive: true,
    appendTo: "parent",
    // hideOnClick: false,
    placement: "bottom-end"
  });
  console.log(event)
  setProps({
    getReferenceClientRect: () => ({
      width: 0,
      height: 0,
      top: event.clientY,
      bottom: event.clientY,
      left: event.clientX,
      right: event.clientX
    })
  });

  show();
  if (isInClose.value) {
    if (!state.value.isShown && !state.value.isVisible) return;
    tippyElRef.value.addEventListener("click", destroy);
    // document.documentElement.addEventListener("click", destroy);
    // useEventListener(tippyElRef, "click", destroy);
  }
}
</script>


<style lang="scss">
@import "cropperjs/dist/cropper.css";

.re-circled {
  .cropper-view-box,
  .cropper-face {
    border-radius: 50%;
  }
}
</style>